j8.prototype.initAst = function() {
  var that = this;

  /* Node */
  this.Node = function() {
    this.type = "Node";
  };

  this.Node.prototype.accept = function(visitor) {
    var script = "visitor.visit" + this.type + "(this);";
    alert(script);
    eval(script);
  };


  /* Statement */
  this.Statement = function() {
    this.type = "Statement";
  };  
  this.Statement.prototype = new this.Node();

  this.Statement.prototype.isEmpty = function() {
    return this.type === "EmptyStatement";
  };


  /* Expression */
  this.Expression = function() {
    this.type = "Expression";
  };

  this.Expression.prototype = new this.Node();
  this.Expression.prototype.isValidLeftSideAssignment = function() { return false; };
  this.Expression.prototype.markAsStatement = function() { /* Do nothing */ };


  /**
   * A sentinel used during pre parsing that represents some expression
   * that is a valid left hand side without having to actually build
   * the expression.
   */  
  this.ValidLeftHandSideSentinel = function() {
    this.type = "ValidLeftHandSideSentinel";
  };

  this.ValidLeftHandSideSentinel.prototype = new this.Expression();
  this.ValidLeftHandSideSentinel.prototype.isValidLeftSideAssignment = function() { return true; };
  this.ValidLeftHandSideSentinel.prototype.accept = function(visitor) { throw "UNREACHABLE"; };
  this.ValidLeftHandSideSentinel.instance = new this.ValidLeftHandSideSentinel();


  /* BreakableStatement */
  this.BreakableStatement = function() {
    this.type = "BreakableStatement";
  };
  this.BreakableStatement.prototype = new this.Statement();


  /* Block */
  this.Block = function(isInitializerBlock) {
    this.type = "Block";
    this.isInitializerBlock = isInitializerBlock;
    this.statements = [];
  };
  this.Block.prototype = new this.BreakableStatement();


  /* Declaration */
  this.Declaration = function(variableProxy, mode, fun) {
    this.type = "Declaration";
    this.variableProxy = variableProxy;
    this.mode = mode;
    this.fun = fun;
  };
  this.Declaration.prototype = new this.Node();


  /* IterationStatement */
  this.IterationStatement = function() {
    this.type = "IterationStatement";
  };
  this.IterationStatement.prototype = new this.BreakableStatement();


  /* LoopStatement */
  this.LoopStatement = function(loopType) {
    this.type = "LoopStatement";
    this.loopType = loopType;
  };
  this.LoopStatement.prototype = new this.IterationStatement();

  this.LoopStatement.prototype.initialize = function(init, cond, next, body) {
    this.init = init;
    this.cond = cond;
    this.next = next;
    this.body = body;
  };


  /* ForInStatement */
  this.ForInStatement = function() {
    this.type = "ForInStatement";
  };
  this.ForInStatement.prototype = new this.IterationStatement();

  this.ForInStatement.prototype.initialize = function(each, enumerable, body) {
    this.each = each;
    this.enumerable = enumerable;
    this.body = body;
  };


  /* ExpressionStatement */
  this.ExpressionStatement = function(expression) {
    this.type = "ExpressionStatement";
    this.expression = expression;
  };
  this.ExpressionStatement.prototype = new this.Statement();


  /* ContinueStatement */
  this.ContinueStatement = function(target) {
    this.type = "ContinueStatement";
    this.target = target;
  };
  this.ContinueStatement.prototype = new this.Statement();


  /* BreakStatement */
  this.BreakStatement = function(target) {
    this.type = "BreakStatement";
    this.target = target;
  };
  this.BreakStatement.prototype = new this.Statement();


  /* ReturnStatement */
  this.ReturnStatement = function(expression) {
    this.type = "ReturnStatement";
    this.expression = expression;
  };
  this.ReturnStatement.prototype = new this.Statement();


  /* WithEnterStatement */
  this.WithEnterStatement = function(expression, isCatchBlock) {
    this.type = "WithEnterStatement";
    this.expression = expression;
    this.isCatchBlock = isCatchBlock;
  };
  this.WithEnterStatement.prototype = new this.Statement();


  /* WithExitStatement */
  this.WithExitStatement = function() {
    this.type = "WithExitStatement";
  };
  this.WithExitStatement.prototype = new this.Statement();


  /* CaseClause */
  this.CaseClause = function(label, statements) {
    this.type = "CaseClause";
    this.label = label;
    this.statements = statements;
  };

  this.CaseClause.prototype.isDefault = function() {
    return this.label == null;
  };


  /* SwitchStatement */
  this.SwitchStatement = function() {
    this.type = "SwitchStatement";
  };
  this.SwitchStatement.prototype = new this.BreakableStatement();

  this.SwitchStatement.prototype.initialize = function(tag, cases) {
    this.tag = tag;
    this.cases = cases;
  };


  /* IfStatement */
  // If-statements always have non-null references to their then- and
  // else-parts. When parsing if-statements with no explicit else-part,
  // the parser implicitly creates an empty statement. Use the
  // HasThenStatement() and HasElseStatement() functions to check if a
  // given if-statement has a then- or an else-part containing code.
  this.IfStatement = function(condition, thenStatement, elseStatement) {
    this.type = "IfStatement";
    this.condition = condition;
    this.thenStatement = thenStatement;
    this.elseStatement = elseStatement;
  };
  this.IfStatement.prototype = new this.Statement();

  this.IfStatement.prototype.hasThenStatement = function() {
    return !this.thenStatement.isEmpty();
  };

  this.IfStatement.prototype.hasElseStatement = function() {
    return !this.elseStatement.isEmpty();
  };


  /* TryCatch */
  this.TryCatch = function(tryBlock, catchVar, catchBlock) {
    this.type = "TryCatch";
    this.tryBlock = tryBlock;
    this.catchVar = catchVar;
    this.catchBlock = catchBlock;
  };
  this.TryCatch.prototype = new this.Statement();


  /* TryFinally */
  this.TryFinally = function(tryBlock, finallyBlock) {
    this.type = "TryFinally";
    this.tryBlock = tryBlock;
    this.finallyBlock = finallyBlock;
  };
  this.TryFinally.prototype = new this.Statement();
  

  /* DebuggerStatement */
  this.DebuggerStatement = function() {
    this.type = "DebuggerStatement";
  };
  this.DebuggerStatement.prototype = new this.Statement();


  /* EmptyStatement */
  this.EmptyStatement = function() {
    this.type = "EmptyStatement";
  };
  this.EmptyStatement.prototype = new this.Statement();


  /* Literal */
  this.Literal = function(value) {
    this.type = "Literal";
    this.value = value;
  };
  this.Literal.prototype = new this.Expression();

  this.Literal.prototype.isIdenticalTo = function(other) {
    return this.value == other.value;
  };

  this.Literal.prototype.isNull = function() { return this.value == null; };
  this.Literal.prototype.isTrue = function() { return typeof(this.value) === "boolean" && this.value; };
  this.Literal.prototype.isFalse = function() { return typeof(this.value) === "boolean" && !this.value; };

  
  /* ObjectLiteral */
  this.ObjectLiteral = function(constantProperties, properties, literalIndex) {
    this.type = "ObjectLiteral";
    this.constantProperties = constantProperties;
    this.properties = properties;
    this.literalIndex = literalIndex;
  };
  this.ObjectLiteral.prototype = new this.Expression();


  /* RegExpLiteral */
  // Node for capturing a regexp literal.
  this.RegExpLiteral = function(pattern, flags, literalIndex) {
    this.type = "RegExpLiteral";
    this.pattern = pattern;
    this.flags = flags;
    this.literalIndex = literalIndex;
  };
  this.RegExpLiteral.prototype = new this.Expression();


  /* ArrayLiteral */
  // An array literal has a literals object that is used
  // for minimizing the work when constructing it at runtime.
  this.ArrayLiteral = function(literals, values) {
    this.type = "ArrayLiteral";
    this.literals = literals;
    this.values = values;
  };
  this.ArrayLiteral.prototype = new this.Expression();


  /* CatchExtensionObject */
  // Node for constructing a context extension object for a catch block.
  // The catch context extension object has one property.
  this.CatchExtensionObject = function(key, value) {
    this.type = "CatchExtensionObject";
    this.key = key;
    this.value = value;
  };
  this.CatchExtensionObject.prototype = new this.Expression();


  /* VariableProxy */
  this.VariableProxy = function(isThis, name, insideWith) {
    this.type = "VariableProxy";
    this.isThis = isThis;
    this.name = name;
    this.insideWith = insideWith;
    this.variable = null; // resolved variable, or null
    this.varUses = 1; // uses of the variable value
    this.objUses = 0; // uses of the object the variable points to
  };
  this.VariableProxy.prototype = new this.Expression();

  this.VariableProxy.prototype.isValidLeftHandSide = function() {
    return this.variable == null ? true : this.variable.isValidLeftHandSide();
  };

  this.VariableProxy.prototype.isVariable = function(n) {
    return !this.isThis && this.name.isIdenticalTo(n);
  };

  this.VariableProxy.prototype.bindTo = function(variable) {
    this.variable = variable;
    this.varUses++;
    this.objUses++;
  };

  this.VariableProxy.thisProxy = new this.VariableProxy(true, null, false);
  this.VariableProxy.identifierProxy = new this.VariableProxy(false, null, false);
  

  /* Slot */
  this.Slot = function(variable, slotType, index) {
    this.type = "Slot";
    this.variable = variable;
    this.slotType = slotType;
    this.index = index;
  };
  this.Slot.prototype = new this.Expression();


  /* Property */
  this.Property = function(obj, key, position) {
    this.type = "Property";
    this.obj = obj;
    this.key = key;
    this.position = position;
  };
  this.Property.prototype = new this.Expression();
  
  this.Property.prototype.isValidLeftHandSide = function() { return true; };
  this.Property.thisProperty = new this.Property(this.VariableProxy.thisProxy, null, 0);

  /* Call */
  this.Call = function(expression, args, position) {
    this.type = "Call";
    this.expression = expression;
    this.arguments = args;
    this.position = position;
  };
  this.Call.prototype = new this.Expression();

  this.Call.sentinel = new this.Call(null, null, 0);

  /* CallNew */
  this.CallNew = function(expression, args, position) {
    this.type = "CallNew";
    this.expression = expression;
    this.args = args;
    this.position = position;
  };
  this.CallNew.prototype = new this.Expression();

  this.CallNew.sentinel = new this.CallNew(null, null, 0);


  /* CallEval */
  // The CallEval class represents a call of the form 'eval(...)' where eval
  // cannot be seen to be overwritten at compile time. It is potentially a
  // direct (i.e. not aliased) eval call. The real nature of the call is
  // determined at runtime.
  this.CallEval = function(expression, args, position) {
    this.type = "CallEval";
    this.expression = expression;
    this.args = args;
    this.position = position;
  };
  this.CallEval.prototype = new this.Expression();

  this.CallEval.sentinel = this.CallEval(null, null, 0);


  /* CallRuntime */
  // The CallRuntime class does not represent any official JavaScript
  // language construct. Instead it is used to call a C or JS function
  // with a set of arguments. This is used from the builtins that are
  // implemented in JavaScript (see "v8natives.js").
  this.CallRuntime = function(name, func, args) {
    this.type = "CallRuntime";
    this.name = name;
    this.func = func;
    this.args = args;
  };
  this.CallRuntime.prototype = new this.Expression();


  /* UnaryOperation */
  this.UnaryOperation = function(op, expression) {
    this.type = "UnaryOperation";
    this.op = op;
    this.expression = expression;
  };
  this.UnaryOperation.prototype = new this.Expression();


  /* BinaryOperation */
  this.BinaryOperation = function(op, left, right) {
    this.type = "BinaryOperation";
    this.op = op;
    this.left = left;
    this.right = right;
  };
  this.BinaryOperation.prototype = new this.Expression();


  /* CountOperation */
  this.CountOperation = function(isPrefix, op, expression) {
    this.type = "CountOperation";
    this.isPrefix = isPrefix;
    this.op = op;
    this.expression = expression;
  };
  this.CountOperation.prototype = new this.Expression();

  this.CountOperation.prototype.isPrefix = function() { return this.isPrefix; };
  this.CountOperation.prototype.isPostfix = function() { return !this.isPrefix; };
  this.CountOperation.prototype.markAsStatement = function() { this.isPrefix = true; };
  

  /* CompareOperation */
  this.CompareOperation = function(op, left, right) {
    this.type = "CompareOperation";
    this.op = op;
    this.left = left;
    this.right = right;
  };
  this.CompareOperation.prototype = new this.Expression();


  /* Conditional */
  this.Conditional = function(condition, thenExpression, elseExpression) {
    this.type = "Conditional";
    this.condition = condition;
    this.thenExpression = thenExpression;
    this.elseExpression = elseExpression;
  };
  this.Conditional.prototype = new this.Expression();


  /* Assignment */
  this.Assignment = function(op, target, value, position) {
    this.type = "Assignment";
    this.op = op;
    this.target = target;
    this.value = value;
    this.position = position;
  };
  this.Assignment.prototype = new this.Expression();
  this.Assignment.prototype.binaryOp = function() {
    switch (this.op.name) {
      case "ASSIGN_BIT_OR": return that.$T("BIT_OR");
      case "ASSIGN_BIT_XOR": return that.$T("BIT_XOR");
      case "ASSIGN_BIT_AND": return that.$T("BIT_AND");
      case "ASSIGN_SHL": return that.$T("SHL");
      case "ASSIGN_SAR": return that.$T("SAR");
      case "ASSIGN_SHR": return that.$T("SHR");
      case "ASSIGN_ADD": return that.$T("ADD");
      case "ASSIGN_SUB": return that.$T("SUB");
      case "ASSIGN_MUL": return that.$T("MUL");
      case "ASSIGN_DIV": return that.$T("DIV");
      case "ASSIGN_MOD": return that.$T("MOD");
      default: throw "UNREACHABLE";
    }
  };


  /* Throw */
  this.Throw = function(exception, position) {
    this.type = "Throw";
    this.exception = exception;
    this.position = position;
  };
  this.Throw.prototype = new this.Expression();


  /* FunctionLiteral */
  this.FunctionLiteral = function(name, scope, body, materializedLiteralCount, containsArrayLiteral,
      expectedPropertyCount, numParameters, startPosition, endPosition, isExpression) {
    this.type = "FunctionLiteral";
    this.name = name;
    this.scope = scope;
    this.body = body;
    this.materializedLiteralCount = materializedLiteralCount;
    this.containsArrayLiteral = containsArrayLiteral;
    this.expectedPropertyCount = expectedPropertyCount;
    this.numParameters = numParameters;
    this.startPosition = startPosition;
    this.endPosition = endPosition;
    this.isExpression = isExpression;
    this.loopNesting = 0;
  };
  this.FunctionLiteral.prototype = new this.Expression();


  /* FunctionBoilerplateLiteral */
  this.FunctionBoilerplateLiteral = function(boilerplate) {
    this.type = "FunctionBoilerplateLiteral";
    this.boilerplate = boilerplate;
  };
  this.FunctionBoilerplateLiteral.prototype = new this.Expression();


  /* ThisFunction */
  this.ThisFunction = function() {
    this.type = "ThisFunction";
  };
  this.ThisFunction.prototype = new this.Expression();

// ----------------------------------------------------------------------------
// Regular expressions

// TODO: implement regular expressions.

// End of regular expressions

  this.AstVisitor = function() {
    this.stackOverflow = false;
  };

  // Dispatch
  this.AstVisitor.prototype.visit = function(node) { node.accept(this); };
  // Iteration
  this.AstVisitor.prototype.visitMany = function(nodes) {
    // TODO: use jQuery's each.
    for (var i = 0; i < nodes.length; i++) {
      if (nodes[i] != null) {
        this.visit(nodes[i]);
      }
    }
  };

  this.AstVisitor.prototype.hasStackOverflow = function() { return this.stackOverflow; };
  this.AstVisitor.prototype.checkStackOverflow = function() {
    if (this.stackOverflow) { return true; }
    // TODO: insert a real check here if needed.
    return false;
  };
  this.AstVisitor.prototype.visitBlock = function() { };                                      
  this.AstVisitor.prototype.visitDeclaration = function() { };
  this.AstVisitor.prototype.visitExpressionStatement = function() { };
  this.AstVisitor.prototype.visitEmptyStatement = function() { };
  this.AstVisitor.prototype.visitIfStatement = function() { };
  this.AstVisitor.prototype.visitContinueStatement = function() { };
  this.AstVisitor.prototype.visitBreakStatement = function() { };
  this.AstVisitor.prototype.visitReturnStatement = function() { };
  this.AstVisitor.prototype.visitWithEnterStatement = function() { };
  this.AstVisitor.prototype.visitWithExitStatement = function() { };
  this.AstVisitor.prototype.visitSwitchStatement = function() { };
  this.AstVisitor.prototype.visitLoopStatement = function() { };
  this.AstVisitor.prototype.visitForInStatement = function() { };
  this.AstVisitor.prototype.visitTryCatch = function() { };
  this.AstVisitor.prototype.visitTryFinally = function() { };
  this.AstVisitor.prototype.visitDebuggerStatement = function() { };
  this.AstVisitor.prototype.visitFunctionLiteral = function() { };
  this.AstVisitor.prototype.visitFunctionBoilerplateLiteral = function() { };
  this.AstVisitor.prototype.visitConditional = function() { };
  this.AstVisitor.prototype.visitSlot = function() { };
  this.AstVisitor.prototype.visitVariableProxy = function() { };
  this.AstVisitor.prototype.visitLiteral = function() { };
  this.AstVisitor.prototype.visitRegExpLiteral = function() { };
  this.AstVisitor.prototype.visitObjectLiteral = function() { };
  this.AstVisitor.prototype.visitArrayLiteral = function() { };
  this.AstVisitor.prototype.visitCatchExtensionObject = function() { };
  this.AstVisitor.prototype.visitAssignment = function() { };
  this.AstVisitor.prototype.visitThrow = function() { };
  this.AstVisitor.prototype.visitProperty = function() { };
  this.AstVisitor.prototype.visitCall = function() { };
  this.AstVisitor.prototype.visitCallEval = function() { };
  this.AstVisitor.prototype.visitCallNew = function() { };
  this.AstVisitor.prototype.visitCallRuntime = function() { };
  this.AstVisitor.prototype.visitUnaryOperation = function() { };
  this.AstVisitor.prototype.visitCountOperation = function() { };
  this.AstVisitor.prototype.visitBinaryOperation = function() { };
  this.AstVisitor.prototype.visitCompareOperation = function() { };
  this.AstVisitor.prototype.visitThisFunction = function() { };



};